package kassiopeia.agent.infrastructure;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.google.common.base.Preconditions;
import kassiopeia.commons.io.Ping;
import kassiopeia.commons.util.DateTimeUtils;

@Component("aliveAgentsHolder")
public class AliveAgentsHolder {

    private static final Logger logger = Logger.getLogger(AliveAgentsHolder.class);

    private Integer pingTimeout;
    private Map<String, Ping> annoucements;

    @Autowired
    public AliveAgentsHolder(@Value("${io.ping.timeout}") Integer pingTimeout) {
        Preconditions.checkState((pingTimeout != null) && (pingTimeout > 0));
        this.pingTimeout = pingTimeout;
        annoucements = new HashMap<String, Ping>();
    }

    public synchronized Ping getLastPingForAgent(String agentId) {
        Preconditions.checkNotNull(agentId);
        return annoucements.get(agentId);
    }

    public synchronized List<Ping> getAliveAgentsPings() {
        return new ArrayList<Ping>(annoucements.values());
    }

    public synchronized List<String> getAliveAgentsIds() {
        return new ArrayList<String>(annoucements.keySet());
    }

    public synchronized void removeDeadAgents() {
        List<Ping> copy = new ArrayList<Ping>(annoucements.values());
        for (Ping pm : copy) {
            if (pm.getCreationDate().getTime() + pingTimeout < DateTimeUtils.getCurrentDate().getTime()) {
                String agentId = pm.getSourceId();
                annoucements.remove(agentId);
                logger.info("Agent with ID #" + agentId + " is DEAD and has been removed from alive agents list");
            }
        }
    }

    public synchronized void ping(Ping ping) {
        String newAgentId = ping.getSourceId();
        if (annoucements.put(newAgentId, ping) == null) {
            logger.info("Welcome NEW agent with ID #" + newAgentId);
        } else {
            logger.debug("Ping received from agent with ID #" + newAgentId);
        }
    }

    public synchronized boolean isAgentAlive(String agentId) {
        return annoucements.containsKey(agentId);
    }

    public synchronized int getAliveAgentsCount() {
        return annoucements.size();
    }
}
